﻿/*
	Copyright (C) 2019 Zongming Liu <1849059316@qq.com>

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with this program; if not, write to the Free Software Foundation, Inc.,
	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#ifndef Node_h__
#define Node_h__

#include "../base/Object.h"
#include "../base/Vector.h"
#include "../base/Types.h"
#include "../base/Matrix.h"
#include "../base/Size.h"
#include "../utility/MacroTool.h"
#include "../utility/SmartPtrDef.h"
#include "../action/IActableObject.h"
#include <list>
#include <string>
#include <memory>
#include <functional>

namespace SkyEngine2d
{

	class Render;
	class Camera;
	class ActionManager;
	class Action;
	class GameWorld;
	class NodeProxy;

	/*!
	 \brief 游戏渲染节点
	 \details
	*/
	class SGE_API Node
		:public IMovableObject, public Object<Node>
	{
		NODE_CLASS(Node)
	public:

		//! 创建一个节点
		/*!
		\param const NodeSharedPtr & parent 父节点 父节点如果为空，将导致节点无法渲染
		\return NodeSharedPtr
		*/
		static Node* create(Node* parent);

		//! 析构
		virtual ~Node();

		//! 是否可见
		/*!
		\return bool
		*/
		bool isVisible()const { return m_visible; }

		//! 设置是否可见
		/*!
		\param bool visible
		\return void
		*/
		void setVisible(bool visible) { m_visible = visible; }

		//! 获取局部的渲染顺序
		/*!
		\return int32_t
		*/
		int32_t getLocalZOrder() const { return m_localZOrder; }

		//! 设置局部的渲染顺序
		/*!
		\param int32_t z 渲染顺序
		\return void
		*/
		void  setLoacalZOrder(int32_t z);

		//! 获取全局的渲染顺序
		/*!
		\return int32_t
		*/
		int32_t getGlobalZOrder()const { return m_globalZOrder; }

		//! 设置全局的渲染顺序
		/*!
		\param int32_t z
		\return void
		*/
		void setGlobalZOrder(int32_t z);

		//! 移动节点的位置
		/*!
		\param float dt_x x的偏移量
		\param float dt_y y的偏移量
		\return void
		*/
		virtual void movePosition(float dt_x, float dt_y);

		//! 移动节点的位置
		/*!
		\param const Vector2 & dt_pos 位置的偏移量
		\return void
		*/
		virtual void movePosition(const Vector2& dt_pos);

		//! 设置节点的位置
		/*!
		\param float x
		\param float y
		\return void
		\note 位置是相对于父节点的
		*/
		virtual void setPosition(float x, float y);

		//! 设置节点的位置
		/*!
		\param const Vector2 & new_pos
		\return void
		\note 位置是相对于父节点的
		*/
		virtual void setPosition(const Vector2& new_pos) override;

		//! 设置节点的锚点
		/*!
		\param const Vector2 & anchor 锚点 值的范围为 [0，1]，默认该属性为（0.0，0.0）
		\return void
		*/
		void setAnchorPoint(const Vector2& anchor);

		//! 设置节点的尺寸 
		/*!
		\param const Size & content_size
		\return void
		*/
		void setContentSize(const Size& content_size);

		//! 获取节点的尺寸
		/*!
		\return const Size&
		*/
		const Size& getContentSize()const;

		//! 设置旋转（弧度）
		/*!
		\param radian 弧度
		\return void
		*/
		virtual void setRotationR(float  radian);

		//!旋转节点(角度) 
		/*!
		\param angle 角度
		\return void
		*/
		virtual void setRotationA(float angle);

		//! 设置缩放 x轴
		/*!
		节点将沿着x轴进行缩放
		\param sx
		\return void
		*/
		virtual void setScalingX(float sx);

		//! 设置缩放 y轴
		/*!
		节点将沿着y轴进行缩放
		\param sy
		\return void
		*/
		virtual void setScalingY(float sy);

		//! 获取位置 
		/*!
		\return Vector2
		*/
		virtual Vector2  getPosition()const override;

		//! 获取旋转的弧度
		/*!
		\return float
		*/
		float getRoattionR()const;

		//! 获取旋转的角度
		/*!
		\return float
		*/
		float getRoattionA()const;

		//! 获取缩放比例 X 轴
		/*!
		\return float
		*/
		float  getScalingX()const;

		//! 获取缩放的比例 Y轴 
		/*!
		\return float
		*/
		float  getScalingY()const;

		//! 矩阵是否改变 
		/*!
		\return bool
		*/
		bool isChangeTransformMat()const;

		//! 获取相机
		/*!
		\return Camera*
		*/
		Camera* getCamera()const;

		//! 设置相机  
		//! 
		/*!
		\param Camera* c 要设置的相机
		\param bool is_recursion 如果有子节点 是否递归的设置子节点
		\return void
		*/
		void setCamera(Camera* c, bool is_recursion = true);

		//! 运行一个动作
		/*!
		\param ActionSharedPtr action 动作
		\return bool 可能失败，如果当前节点不支持该动作
		*/
		bool runAction(ActionSharedPtr action);

		//! 停止一个动画，通过名字
		/*!
		\param const std::wstring & action_name
		\return void
		*/
		void stopActionByName(const std::wstring& action_name);

		//! 通过名字获取当前正在运行的动画
		/*!
		\param const std::wstring & name 动画的名字
		\return ActionSharedPtr
		*/
		ActionSharedPtr getActionByName(const std::wstring& name);

	public:
		//! 安装监听器在节点的位置即将改变时
		/*!
		\param const std::function<(Node * node)>& call_func 监听器回掉
		\return void
		监听器 传入一个 即将要变更的 位置值的引用 ，用户可以通过这个引用来修正节点的位置
		*/
		void installListenerAtPosChangeBefore(const std::function<void(Vector2& future_position)>& call_func);

		//! 安装监听器在节点的位置改变之后
		/*!
		\param const std::function<void
		\param const std::function<void(const Vector2& old_pos,const Vector2& now_pos)>& call_func 监听器回掉
		\return void
		监听器传入 之前的位置 和 现在的位置
		*/
		void installListenerAtPosChangeAfter(const std::function<void(const Vector2& old_pos, const Vector2& now_pos)>& call_func);

	protected:

		//! 构造
		/*!
		\param const std::shared_ptr<Node> & parent 父节点，如果父节点为null 这会导致无法渲染到该节点
		*/
		Node(Node* parent);

		//! 初始化
		/*!
		\return bool
		*/
		virtual bool init() override;

		//! 绘制这个节点
		/*!
		\param Render * render 渲染器
		\param const Matrix4X4 & transform 变换矩阵
		\return void
		*/
		virtual void draw(IRender* render, const Matrix4X4& transform);

		//! 访问节点
		/*!
		\param Render * render 渲染器
		\param bool parent_change_transfrom_mat 父节点的矩阵是否发生变化
		\return void
		*/
		virtual void visit(IRender* render, bool parent_change_transfrom_mat);

		virtual void paused();

		virtual void resume();
	protected:
		//! 将世界坐标装换为D3d渲染坐标
		Vector2 convertToRenderCoordinate(const Vector2& v);
		//! 更新矩阵
		void  updateTransformMat(bool prant_transformMat_is_change);
		//! 矩阵发生改变
		void changeTransformMat() { m_isChangeTransforMat = true; }
		//! 获取父节点的TransformMat
		const Matrix4X4& getParentTransformMat()const;

	protected:
		//仿射矩阵
		Matrix4X4 m_transformMat;
	private:

		//是否可以显示
		bool m_visible;

		int32_t m_localZOrder;
		int32_t m_globalZOrder;

		bool m_isNeedSort;

		//位置
		Vector2 m_position;
		//旋转  弧度
		float  m_rotation;
		//缩放
		float m_scalingX;
		float m_scalingY;

		//相机
		Camera* m_camera;
		//是否发生新的变换
		bool m_isChangeTransforMat;

		ActionManager* m_actionManager;

		GameWorld* m_gameWorld;

		//是否正在运行 
		bool m_running;

		//节点的尺寸
		Size m_contentSize;
		//节点的尺寸是否发生变化
		bool m_isChangeContentSize;

		//锚点在节点中的真实偏移
		Vector2 m_anchorPointInContentSize;
		//锚点 [0,1]
		Vector2 m_anchorPoint;

		//---------------------------
		//节点的一些监控
		//-----------------------------------

		//在位置改变之前
		std::unique_ptr<std::function<void(Vector2& position)>> m_onPositionChangeBefore;
		//位置改变之后
		using FuncAtPosChangeAfter = std::function<void(const Vector2& old_pos, const Vector2& now_pos)>;
		std::unique_ptr<FuncAtPosChangeAfter> m_onPositionChangeAfter;
		//节点进入时
		std::unique_ptr<std::function<void()>> m_onEnter;
		//节点退出后
		std::unique_ptr<std::function<void()>> m_onExit;

	private:
		friend class NodeProxy;
	};

	//! 节点的代理
	class NodeProxy
		:public ObjectProxy<Node>
	{
	public:
		//在加入节点时调用
		virtual void onEnter(Node* target) override;
		//在移除节点时调用
		virtual void onExit(Node* target) override;
	};

}

#endif // Node_h__
